/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __ATOM_H__
#define __ATOM_H__

#include <Converter.h>
#include <string>
#include <threads/Lock.h>
#include <map>
#include <vector>
#include <StaticRefCounted.h>

namespace liba
{

/** Atom is the string, optimized for copying and comparison.
	Atoms are not case-sensitive.
	Construct widely used atoms as public statics in appropriate classes. */
class AtomStatics
{
	typedef std::map<std::string,int> MapType;
	typedef std::vector<std::string> VectorType;
	MapType MapGlob;
	VectorType VectorGlob;
	threads::Mutex MutexGlob;
	int AtomNextValue;

public:
	AtomStatics();
	~AtomStatics();

	int find_value( const std::string & str );

	const std::string & get_str(int value)const;
#ifdef VLD_DETECT_LEAKS
	void FreeStrings();
#endif
};

class Atom : public StaticRefCounted<AtomStatics>
{
public:
	Atom();
	explicit Atom(const std::string & name);
	Atom(const char * begin, const char * end);

	bool operator>(const Atom & atom)const;
	bool operator<(const Atom & atom)const;
	bool operator>=(const Atom & atom)const;
	bool operator<=(const Atom & atom)const;
	bool operator==(const Atom & atom)const;
	bool operator!=(const Atom & atom)const;

	/** This method is very effective. */
	const std::string & get_str()const;
private:
	int value;
	/** @todo This is here because Microsoft Visual Studio 6 does not allow to
		evaluate functions during debugging. */
#ifdef _DEBUG
	std::string debug_value;
#endif
public:
#ifdef VLD_DETECT_LEAKS
	void FreeStrings();
#endif

public:
	template<typename Sym>
	struct ConverterBuffer
	{
		bool convert(const Atom & value)
		{
			return converter::convert( value.get_str(), &data );
		}
		const Sym * begin()const
		{
			return data.c_str();
		}
		const Sym * end()const
		{
			return data.c_str() + data.size();
		}
	private:
		std::basic_string<Sym> data;
	};

	template<class Sym>
	bool convert(const Sym * be, const Sym * en)
	{
		std::string char_str;
		if( !converter::convert( std::basic_string<Sym>(be, en), &char_str ) )
			return false;

		*this = Atom( char_str );
		return true;
	}
};

inline Atom::Atom()
:	value( 0 )
{}

inline bool Atom::operator>(const Atom & atom)const
{
	return value > atom.value;
}

inline bool Atom::operator<(const Atom & atom)const
{
	return value < atom.value;
}

inline bool Atom::operator>=(const Atom & atom)const
{
	return value >= atom.value;
}

inline bool Atom::operator<=(const Atom & atom)const
{
	return value <= atom.value;
}

inline bool Atom::operator==(const Atom & atom)const
{
	return value == atom.value;
}

inline bool Atom::operator!=(const Atom & atom)const
{
	return value != atom.value;
}

template<>
struct Atom::ConverterBuffer< char >
{
	bool convert(const Atom & value)
	{
		data = value;
		return true;
	}
	const char * begin()const
	{
		return data.get_str().c_str();
	}
	const char * end()const
	{
		return data.get_str().c_str() + data.get_str().size();
	}
private:
	Atom data;
};

namespace converter
{

template<>
struct ConverterBestCharSelector< Atom >
{
	typedef char BestChar;
};

} //namespace converter

} // namespace liba
using namespace liba;

#endif  //__ATOM_H__